package modulesAPI.v2;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import com.caterina.components.CButton;
import com.caterina.components.CLabel;
import com.caterina.components.CLabel.LabelType;
import com.caterina.components.CPanel;
import com.caterina.resources.CColors;
import com.caterina.resources.CWords;

public abstract class Module<T> implements ModuleChangedListener{
	/**
	 * Crea un nuovo modulo, questa classe deve essere estesa per essere istanziata
	 * */
	public Module(Module<?> ownerModule,T content,String tag,String description,ViewType viewType,int...options){
		this.content=content;
		this.tag=tag;
		this.options=options;
		this.viewType=viewType;
		this.description=description;
		this.listeners=new ArrayList<ModuleChangedListener>();
		this.ownerModule=ownerModule;
		if (ownerModule!=null){
			this.hierachyNumber=this.ownerModule.getHierachyNumber()+1;
			this.addModuleChangedListener(ownerModule);
		}
	}
	/**
	 * Aggiunge pulsanti visibili in modalità di scrittura del modulo
	 * */
	public void addWritableButton(CButton... btn){
		if (this.canAddWButtons){
			this.wButtons=new ArrayList<CButton>();
			for(CButton btns:btn){
				this.wButtons.add(btns);
			}
		}
		this.canAddWButtons=false;
	}
	/**
	 * Aggiunge pulsanti visibili in modalità di sola lettura del modulo
	 * */
	public void addButtons(CButton... btn){
		if (this.canAddButton){
			this.buttons=new ArrayList<CButton>();
			for(CButton btns:btn){
				this.buttons.add(btns);
			}
		}
		this.canAddButton=false;
	}
	/**
	 * Aggiunge moduli affiliati al presente modulo, organizzati gerarchicamente
	 * */
	public void addModules(Module<?>... modules){
		if (canAdd){
			this.modules=new ArrayList<Module<?>>();
			for(Module<?> mod:modules){
				this.modules.add(mod);
			}
			canAdd=false;
		}
	}
	/**
	 * Aggiunge un listener che tiene traccia delle modifiche di questo modulo
	 * */
	public void addModuleChangedListener(ModuleChangedListener mchl){this.listeners.add(mchl);}
	/**
	 * Avvisa i listener che questo modulo, oppure gli affiliati di questo modulo, sono stati modificati
	 * */
	public void fireModuleChanged(Module<?> mod){
		try{
			for (ModuleChangedListener mchl:listeners)mchl.onModuleChange(mod);
		}catch(ConcurrentModificationException e){}
	}
	/**
	 * Rimuove un listener di modifica del modulo e affiliati
	 * */
	public void removeModuleChangedListener(ModuleChangedListener mchl){this.listeners.remove(mchl);}
	/**
	 * Controlla se un opzione di modulo è abilitata
	 * */
	public boolean isOptionEnabled(int option){
		for(int opt:this.options)if(opt==option)return true;
		return false;
	}
	/**
	 * Indica se il modulo non è composto da altri moduli, quindi rappresenta un informazione elementare
	 * */
	public boolean isBasic(){
		if (this.getHierachyNumber()==-1)return true;
		else return false;
	}
	/**
	 * Indica se il modulo è visibile o meno, questa informazione viene propagata anche per gli avviati
	 */
	public boolean isVisible(){
		return this.visible;
	}
	/**
	 *  Ritorna la versione tradotta dell' etichetta applicata al modulo
	 */
	public String getTag(){return this.getTag(true);}
	public String getTag(boolean translate){
		if(!translate)return this.tag;
		else return CWords.getWord(this.tag);
	}
	/**
	 * Ritorna il modulo padre di questo modulo
	 */
	public Module<?>getOwnerModule(){
		return this.ownerModule;
	}
	/**
	 * Ritorna il primo modulo della gerarchia
	 */
	public Module<?>getAncestorModule(){
		Module<?> tmpMod=this;
		while(tmpMod.getOwnerModule()!=null){
			tmpMod=tmpMod.getOwnerModule();
		}
		return tmpMod;
	}
	/**
	 * Ritorna la versione tradotta della descrizione
	 */
	public String getDescription(){return CWords.getWord(this.description);}
	/**
	 * Ritorna il numero gerarchico di questo modulo
	 */
	public int getHierachyNumber(){return hierachyNumber;}
	/**
	 * Ritorna il contenuto (Modello) di questo modulo
	 */
	public T getContent(){return this.content;}
	/**
	 * Imposta la visibilità di questo modulo
	 */
	public void setVisible(boolean visible){
		this.visible=visible;
	}
	/**
	 * Ritorna il tipo di vista di questo modulo
	 */
	public ViewType getViewType(){
		return this.viewType;
	}
	/**
	 * Ritorna il path gerarchico di questo modulo
	 * es: se ci troviamo nel modulo customer, e questo si trova in una richiesta
	 *  questa funzione ritornerà:
	 * "Request/Customer"
	 */
	public String getPath(){
		Module<?> temp=this;
		ArrayList<Module<?>> stack=new ArrayList<Module<?>>();
		stack.add(temp);
		while((temp=temp.getOwnerModule())!=null){
			stack.add(temp);
		}
		StringBuilder sb=new StringBuilder();
		for(int i=stack.size()-1;i>=0;i--){
			sb.append(stack.get(i).getTag()+"/");
		}
		return sb.toString();
	}
	/**
	 *Ritorna il pannello generato da questo modulo, in base al contenuto, questo metodo
	 *è chiamato dall' interfaccia grafica, in quanto non ha conoscenza dei modelli
	 */
	public CPanel getPanel(){return this.getPanel(this.getViewType(), false);}
	public CPanel getPanel(ViewType viewType,boolean isSelected){
		if(this.isVisible())
		switch(viewType){
			case HIDDEN: return null;
			case ONE_LINE:return this.getOneLinePanel(isSelected);
			case READ_ONLY: return this.getReadOnlyPanel(isSelected);
			case WRITABLE: return this.getWritablePanel(isSelected);
			case IN_LIST: return this.getInListPanel(isSelected);
			default: return null;
		}
		else return null;
	}
	/**
	 *Ritorna il pannello generato da questo modulo, in base al contenuto,secondo il layout
	 *di default questo metodo
	 *è chiamato dall' interfaccia grafica, in quanto non ha conoscenza dei modelli
	 */
	public CPanel getDefaultPanel(){
		CPanel retInPanel=new CPanel();  
		retInPanel.setLayout(new BoxLayout(retInPanel,BoxLayout.Y_AXIS));
		if (this.getTitlePanel()!=null)
			retInPanel.add(this.getTitlePanel());
		for(Module<?> mod:modules){
			if (mod.getPanel()!=null)retInPanel.add(mod);
		}
		if (this.hierachyNumber==0){
			CPanel retMainPanel=new CPanel(new BorderLayout());
			CPanel retTopPanel=new CPanel(new BorderLayout());
			retTopPanel.add(retInPanel,BorderLayout.WEST);
			retMainPanel.add(retTopPanel,BorderLayout.NORTH);
			retMainPanel.add(new CPanel(),BorderLayout.CENTER);
			retMainPanel.add(this.createButtonsBar(),BorderLayout.SOUTH);
			return retMainPanel;
		}else return retInPanel;
	}
	@Override
	public void onModuleChange(Module<?> mod){
		this.beforeFireChange(mod);
		if(!ignoreFire){
			this.fireModuleChanged(mod);
			this.afterFireChange(mod);
		}
		ignoreFire=false;
	}
	protected void setContent(T content) {
		this.content = content;
	}
	
	protected Collection<Module<?>> getModules(){
		return modules;
	}
	protected Module<?> getModule(int index) {
		return this.modules.get(index);
	}
	protected CPanel getFormattedTitle(){
		CPanel titlePanel=new CPanel();
		if (this.hierachyNumber==0)titlePanel.add(new CLabel(this.getTag(),LabelType.H1),BorderLayout.CENTER);
		if (this.hierachyNumber==1){
			titlePanel.setLayout(new BorderLayout());
			titlePanel.add(new CLabel(this.getTag(),LabelType.H2),BorderLayout.WEST);
		}
		return titlePanel;
	}
	protected void ignoreFire(){
		this.ignoreFire=true;
	}
	/**Viene chiamato prima che i moduli padri e i moduli listener vengano avvisati delle modifiche*/
	protected abstract void beforeFireChange(Module<?> mod);
	/**Viene chiamato dopo che i moduli padri e i moduli listener vengano avvisati delle modifiche*/
	protected abstract void afterFireChange(Module<?> mod);
	protected abstract CPanel getInListPanel(boolean isSelected);
	/**Ottiene un pannello di sola lettura*/
	protected abstract CPanel getReadOnlyPanel(boolean isSelected);
	/**Ottiene un pannello scrivibile*/
	protected abstract CPanel getWritablePanel(boolean isSelected);
	/**Ottiene un pannello su una linea*/
	protected abstract CPanel getOneLinePanel(boolean isSelected);
	/**Ottiene il pannello che rappresenta il titolo*/
	protected CPanel getTitlePanel(){
		CPanel title=new CPanel(new BorderLayout());
		if (this.hierachyNumber==0){
			title.add(new CLabel(this.getTag(),LabelType.H1),BorderLayout.CENTER);
			return title;
		}if (this.hierachyNumber==1){
			title.add(new CLabel(this.getTag(),LabelType.H2));
			title.setPreferredSize(new Dimension(200,30));
			return title;
		}
		return null;
	}
	private CPanel createButtonsBar(){
		CPanel retPanel=new CPanel();
		retPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, CColors.BUTTON_PRESSED_CATERINA));
		if (this.viewType==ViewType.WRITABLE){
			if (this.wButtons!=null)
				for(CButton btn:this.wButtons)
					retPanel.add(btn);
		}
		else{
			if (this.buttons!=null)
				for(CButton btn:this.buttons)
					retPanel.add(btn);
		}
		return retPanel;
	}
	public void setModifyModeEnabled(boolean mode){
		this.modifyMode=mode;
	}
	public boolean isOnModifyModeEnabled(){
		return modifyMode;
	}
	private boolean modifyMode=false;
	private boolean canAdd=true;
	private boolean canAddButton=true;
	private boolean canAddWButtons=true;
	private boolean visible=true;
	private boolean ignoreFire=false;
	private String description;
	private String tag;
	private Module<?> ownerModule;
	private ViewType viewType;
	private ArrayList<ModuleChangedListener>listeners;
	private int hierachyNumber=0;
	private ArrayList<Module<?>> modules;
	private ArrayList<CButton>buttons;
	private ArrayList<CButton> wButtons;
	private int[]options;
	private T content;
}
